#define __LIBRARY__
#include <unistd.h>
#include <time.h>

inline _syscall0(int, fork)
inline _syscall0(int, pause)
inline _syscall0(int, print)

#include <linux/tty.h>
#include <linux/sched.h>
#include <asm/system.h>
#include <asm/io.h>

extern void init(void);
extern void mem_init(long start, long end);
extern long kernel_mktime(struct tm *tm);
extern int printk(const char *fmt, ...);

#define EXT_MEM_K (*(unsigned short *)0x90002)

#define CMOS_READ(addr) ({ \
    outb_p(addr, 0x70);    \
    inb_p(0x71); \
})

#define BCD_TO_BIN(val) ((val) = ((val) & 15) + ((val) >> 4) * 10)

static void time_init(void)
{
    struct tm time;

    do {
        time.tm_sec = CMOS_READ(0);
        time.tm_min = CMOS_READ(2);
        time.tm_hour = CMOS_READ(4);
        time.tm_mday = CMOS_READ(7);
        time.tm_mon = CMOS_READ(8);
        time.tm_year = CMOS_READ(9);
    } while (time.tm_sec != CMOS_READ(0));
    BCD_TO_BIN(time.tm_sec);
    BCD_TO_BIN(time.tm_min);
    BCD_TO_BIN(time.tm_hour);
    BCD_TO_BIN(time.tm_mday);
    BCD_TO_BIN(time.tm_mon);
    BCD_TO_BIN(time.tm_year);
    time.tm_mon--;    // 月份原本的数值范围为1-12，现在变为0-11
    startup_time = kernel_mktime(&time);
}

static long memory_end = 0;
static long buffer_memory_end = 0;
static long main_memory_start = 0;

void main(void)
{
    memory_end = (1 << 20) + (EXT_MEM_K << 10);
    memory_end &= 0xfffff000;
    if (memory_end > 16 * 1024 * 1024)
        memory_end = 16 * 1024 * 1024;
    if (memory_end > 12 * 1024 * 1024) 
        buffer_memory_end = 4 * 1024 * 1024;
    else if (memory_end > 6 * 1024 * 1024)
        buffer_memory_end = 2 * 1024 * 1024;
    else
        buffer_memory_end = 1 * 1024 * 1024;
    main_memory_start = buffer_memory_end;
    mem_init(main_memory_start, memory_end);
    trap_init();
    tty_init();
    time_init();
    sched_init();
    buffer_init(buffer_memory_end);
    sti();
    move_to_user_mode();
    if (!fork()) {
        init();
    }
    while (1)
        pause();
}

void sleep_on(struct task_struct **p);
void wake_up(struct task_struct **p);

int sys_print()
{
    int i;
    static cnt = 0;
    for (i = 0; i < NR_TASKS; i++)
        if (current == task[i]) {
            printk("Now in task %d\n\r", i);
            break;
        }
    cnt++;
    if (cnt == 5)
        sleep_on(&task[2]);
    else if (cnt == 10)
        wake_up(&task[2]);
    return 0;
}

void init2(void)
{
    int i = 0;
    while (1) {
        if (i == 6)
            _exit(0);
        print();
        for (int i = 0; i < 20000000; i++)
            nop();
        i++;
    }
}

void init(void)
{
    if (!fork())
        init2();
    while (1) {
        print();
        for (int i = 0; i < 20000000; i++)
            nop();
    }
}
